# -*- coding: utf-8 -*-
"""
@time：  2021/04/27  19:32
@Auto：Mr. xingye  993843278
@Company 星野训练营 99384378@qq.com
@Function:WEB关键字库

"""
import re
import pytest
import allure
from datetime import date,timedelta
import sys
from selenium import webdriver
from common.ver.verify import Verify
from common.Logger import logger
import time
from selenium.webdriver.support.select import Select
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By



class WEB:
    """关键字类"""

    def __init__(self):
        # 代表当前的浏览器类型
        self.br = 'Chrome'
        # 运行自动化的驱动对象
        self.driver: webdriver.Chrome = None

        # 关联的字段
        self.relations = {}
        # relations =  {"ver1":"FBD1"}
        #
        self.verify = Verify('wuqingfqng', '6e8ebd2e301f3d5331e1e230ff3f3ca5', '96001')

    def Open_Browser(self, url: str ='', brower='Chrome'):
        """
        打开浏览器
        :param brower: Chrome:谷歌浏览器，Firefox:火狐浏览器，Ie:ie浏览器
        :return:无
        """
        if brower.title() == "Chrome" or brower == "":
            self.driver = webdriver.Chrome()
        elif brower.title() == "Firefox":
            self.driver = webdriver.Firefox()
        elif brower.title() == "Ie":
            self.driver = webdriver.Ie()
        else:
            logger.error("浏览器类型暂不支持")
        self.driver.maximize_window()
        self.driver.implicitly_wait(20)
        #打开浏览器
        self.driver.get(url)
        self.__attach_info('', url + ' ' + brower,'Opening browser \'' + brower + '\' to base url \'' + url + '\'.')

    def __find(self, locator: str = ''):
        """
        统一定位关键字
        :param locator: 元素定位器，支持四种常用定位
        :return: 定位到的元素，如果没有定位到则为None
        """
        if locator == None:
            return None
        # xpath
        elif locator.startswith('//') or locator.startswith('xpath='):
            if locator.startswith('//'):
                return self.driver.find_element_by_xpath(locator)
            else:
                locator = locator[locator.find('xpath=') + 6:locator.__len__()]
                return self.driver.find_element_by_xpath(locator)
        # css定位
        elif locator.startswith('#') or locator.startswith('>') or locator.startswith("css="):
            if locator.startswith('css='):
                locator = locator[locator.find('css=') + 4:locator.__len__()]
                return self.driver.find_element_by_css_selector(locator)
            else:
                return self.driver.find_element_by_css_selector(locator)
        # id定位
        elif locator.startswith('id='):
            locator = locator[locator.find('id=') + 3:locator.__len__()]
            return self.driver.find_element_by_id(locator)
        # name定位
        elif locator.startswith('name='):
            locator = locator[locator.find('name=') + 5:locator.__len__()]
            return self.driver.find_element_by_name(locator)
        else:
            logger.warning("温馨提示，目前只支持Xpath、Css、Id、name方式定位...")


    def Input_Text(self, locator: str = '', value: str = ''):
        """
        文本输入
        :param locator: 定位器
        :param value: 需要输入的文本值
        :return: 无
        """
        temp1 = locator
        temp2 = value
        value = self.__get_relations(value)
        ele = self.__find(locator)
        ele.send_keys(value)
        self.__attach_info('', temp1 + ' ' + temp2, 'Typing text \'' + value + '\'into text field \'' + locator + '\'.')

    def Click(self, locator: str = ''):
        """
        点击元素
        :param locator: 定位器
        :return: 无
        """
        temp = locator
        locator = self.__get_relations(locator)
        ele = self.__find(locator)
        ele.click()
        self.__attach_info('', temp, 'Clicking element \'' + locator + '\'.')

    def Clickjs(self, locator: str = ''):
        """
        通过js来点击
        :param locator:定位器
        :return: 无
        """
        temp = locator
        locator = self.__get_relations(locator)
        ele = self.__find(locator)
        self.driver.execute_script('arguments[0].click()', ele)
        self.__attach_info('', temp, 'Clicking element \'' + locator + '\'.')

    def Switch_Browser(self, index: int = -1):
        """
        根据窗口下标切换窗口
        :param index: 句柄的序号，-1代表为最新的窗口，1代表第一次打开的窗口
        :return:
        """
        handle = self.driver.window_handles
        logger.info(handle)
        self.driver.switch_to.window(handle[index])

    def Send(self, locator: str = '', key: str = ''):
        """
        模拟按键输入
        :param locator:定位器
        :param key: 按键操作表达式
        :return: 无
        """
        ele = self.__find(locator)
        ele.send_keys(eval(key))

    def Select_Frame(self, locator: str = ''):
        """
        进入iframe框架
        :param locator:定位器
        :return:无
        """
        ele = self.__find(locator)
        self.driver.switch_to.frame(ele)
        self.__attach_info('', locator, 'Selecting frame \'' + locator + '\'')

    def Unselect_Frame(self):
        """
        退出iframe框架
        """
        self.driver.switch_to.default_content()
        self.__attach_info('','','')

    def Sleep(self, t: int = 2):
        """
        固定等待
        :param t: 等待时间，默认为2s
        :return:
        """
        t = int(t)
        time.sleep(t)
        self.__attach_info('', t, 'Sleep ' + str(t) + ' second')

    def __get_relations(self, params: str = ''):
        """
        获取关联的信息
        :param params:需要关联的参数
        :return:返回关联后的字符串
        """
        for key in self.relations:
            params = params.replace('${' + key + '}', self.relations.get(key))
        return params

    def Close(self):
        """
        关闭当前浏览器
        :return:
        """
        self.driver.close()

    def quit(self):
        """
        退出浏览器
        :return:
        """
        self.driver.quit()

    def Select_List(self, locator: str = '', value: str = ''):
        """
        Select标签下拉框选择，支持文本和value形式
        :param locator: 定位器
        :param value: value值或者text
        :return:
        """

        ele = self.__find(locator)
        select = Select(ele)
        try:
            # 根据value值来选择
            int(value)
            select.select_by_value(value)
        except Exception as e:
            # 根据文本来选择
            select.select_by_visible_text(value)

    def Moveto_Element(self, locator: str = ''):
        """
        将鼠标悬停在元素上
        :param locator: 定位器
        :return:
        """
        ele = self.__find(locator)
        action = ActionChains(self.driver)
        # perform代表使moveto生效
        action.move_to_element(ele).perform()

    def Double_Click(self, locator: str = ''):
        """
        双击元素
        :param locator: 定位器
        :return:
        """
        temp = locator
        ele = self.__find(locator)
        action = ActionChains(self.driver)
        action.double_click(ele).perform()
        self.__attach_info('', temp, 'Clicking element \'' + locator + '\'.')

    def Scroll_Top(self):
        """
        浏览器置顶
        :return:
        """
        jsStr = "window.scrollTo(0,0)"
        self.driver.execute_script(jsStr)

    def Scroll_bottom(self):
        """
        浏览器滑动到底部
        :return:
        """
        jsStr = "window.scrollTo(0,document.body.scrollHeight)"
        self.driver.execute_script(jsStr)




    def Scroll_Element_Into_View(self, locator: str = ''):
        """
        通过js滑动到可见的元素中去
        :param locator: 元素定位器
        :return:
        """
        ele = self.__find(locator)
        self.driver.execute_script('arguments[0].scrollIntoView();', ele)

    def Assign_ID_to_Element(self, locator: str = '', id: str = ''):
        """
        给标签赋值id属性
        :param locator: 定位器
        :param id: 定义临时id的属性值
        :return:
        """
        ele = self.__find(locator)
        self.driver.execute_script(f"arguments[0].id = '{id}';", ele)

    def Execute_Script(self, js: str = ''):
        """
        通过js实现操作,兼容返回值暂未实现
        :param js: js表达式
        :return:
        """
        # 关联参数
        js = self.__get_relations(js)
        self.driver.execute_script(js)

    def Alert_Input(self,value: str = ''):
        """
        alter弹窗输入内容
        :param value: 要输入alter框的内容
        :return:
        """
        self.__get_relations(value)
        self.driver.switch_to.alert.send_keys(value)

    def Alert_Accept(self):
        """
        alter弹窗点击确定
        :return:
        """
        self.driver.switch_to.alert.accept()

    def Alert_Dismiss(self):
        """
        alter弹窗点击取消
        :return:
        """
        self.driver.switch_to.alert.dismiss()

    def Get_Rex_Text(self, key: str = '', locator: str = '', reg: str = ''):
        """
        通过正则表达式的方式获取需要提取的文本内容
        :param key: 保存文本的键
        :param locator: 定位表达式，支持xpath，id，name,css
        :param reg: 提取的正则表达式
        :return:
        """
        ori_key = key
        ori_locator = locator
        try:
            ele = self.__find(locator)
            text = ele.text
            text = re.findall(reg, text)[0]
            #去除符号
            key = key.replace('${','').replace('}','')
            self.relations[key] = text
            logger.info("文本{} 已成功提取".format(self.relations[key]))
            self.__attach_info(ori_key, ori_locator, text)
        except Exception as e:
            self.__attach_info(ori_key, ori_locator, 'Text acquisition failed')
            logger.exception(e)
            self.__get_screenshot()
            pytest.fail()


    def Get_Text(self, key: str = '', locator: str = ''):
        """
        获取文本全部内容
        :param key: 保存文本的键
        :param locator: 定位表达式，支持xpath，id，name,css
        :return:
        """
        ori_key = key
        ori_locator = locator
        try:
            ele = self.__find(locator)
            text = ele.text
            #去除符号
            key = key.replace('${','').replace('}','')
            self.relations[key] = text
            logger.info("文本{} 已成功提取".format(self.relations[key]))
            self.__attach_info(ori_key, ori_locator, text)
        except Exception as e:
            self.__attach_info(ori_key, ori_locator, 'Text acquisition failed')
            logger.exception(e)
            self.__get_screenshot()
            pytest.fail()

    def Get_Current_Url(self, key: str =''):
        """
        获取当前页面的url
        :param key: 保存url的变量（键）
        :return:
        """
        temp = key
        try:
            key = key.replace('${','').replace('}','')
            self.relations[key] = self.driver.current_url
            logger.info("url: {} 已成功获取".format(self.relations[key]))
            self.__attach_info(temp, '', self.relations[key])
        except Exception as e:
            self.__attach_info(temp, '', 'URL acquisition failed')
            logger.exception(e)
            self.__get_screenshot()
            pytest.fail()

    def Get_Value(self,key: str='', locator: str='',attar: str =''):

        #去除符号
        ori_key = key
        temp1 = locator
        temp2 = attar
        try:
            key = key.replace('${','{').replace('}','')
            ele = self.__find(locator)
            value = ele.get_attribute(attar)
            self.relations[key] = value
            logger.info("{}的属性值为{},已成功获取".format(attar,value))
            self.__attach_info(ori_key, temp1 + ' ' + temp2, value)
        except Exception as e:
            self.__attach_info(ori_key, temp1 + ' ' + temp2, 'Failed to get value')
            logger.exception(e)
            self.__get_screenshot()
            pytest.fail()


    def Get_Alter_Message(self, key:str = ''):
        """
        获取弹窗文本
        :param key:保存弹框文本变量（键）
        :return: 文本
        """
        temp = key
        try:
            #去除符号
            key = key.replace('${','').replace('}','')
            WebDriverWait(self.driver, 10, 0.5).until(EC.alert_is_present(), message='没有出现指定弹窗')
            msg = EC.alert_is_present()(self.driver).text
            self.relations[key] = msg
            self.__attach_info(temp, '', msg)
            logger.info("alter内容获取成功：" + str(msg))
        except Exception as e:
            self.__attach_info(temp,'', 'Alter content acquisition failed')
            logger.exception(e)
            self.__get_screenshot()
            pytest.fail()

    def Get_Title(self, key: str = ''):
        """获取当前title"""
        temp = key
        key = key.replace('}', '').replace('${', '')
        title = self.driver.title
        self.relations[key] = title
        # 报告信息输出
        self.__attach_info(temp,'',title)

    def Log(self, text: str = ''):
        """
        日志
        :param text: 日志输出内容
        :return:
        """
        temp = text
        text = self.__get_relations(text)
        self.__attach_info('', temp, text)

    def Comment(self, title: str = '', text: str = ''):
        """这是注解，什么都不需要做"""
        if title and text:
            allure.attach(text, title, allure.attachment_type.TEXT)

    def Forward(self):
        """
        浏览器前进
        :return:
        """
        self.driver.forward()

    def Back(self):
        """
        浏览器后退
        :return:
        """
        self.driver.back()

    def Clear(self, locator: str =''):
        """
        清除文本框
        :param locator: 定位器
        :return:
        """
        ele = self.__find(locator)
        ele.clear()

    def Refresh(self):
        """
        刷新当前页面
        :return:
        """
        self.driver.refresh()




    def __attach_info(self, var: str = '', ori: str = '', *params):
        """
        allure 附件INFO输出
        :param var: 没有经过处理的返回变量名
        :param ori: 没有经过处理的请求参数
        :param params: 处理后的请求参数
        :return:
        """
        if len(var) > 0:
            var = var + ' = '
        allure.attach(var + str(*params),
                      var + self.__class__.__name__ + '.' + sys._getframe().f_back.f_code.co_name + ' ' + str(ori),
                      allure.attachment_type.TEXT)

    # ------------------------------------------------特殊关键字处理———————————————————————————

    def get_Day_Of_Day(self,key: str = '', n: int = 0):
        """获取日期"""
        key = key.replace('}','').replace('${','')
        if (int(n) < 0):
            n = abs(int(n))
            self.relations[key] = str(date.today() - timedelta(days=int(n)))
            allure.attach("{}保存成功".format(self.relations[key]), "保存成功", allure.attachment_type.TEXT)
        else:
            self.relations[key] = str(date.today() + timedelta(days=int(n)))
            allure.attach("{}保存成功".format(self.relations[key]), "保存成功", allure.attachment_type.TEXT)


    # ---------------------------------------------------截图——————————————————————————————
    def __get_screenshot(self):
        """
        allure报告错误截图
        :return:
        """
        time.sleep(2)
        allure.attach(self.driver.get_screenshot_as_png(), "错误截图", allure.attachment_type.PNG)

    # ----------------------------------------------------断言-------------------------------------------------


    def Wait_Until_Page_Contains_Element(self, locator: str = '', timeout: int = 5, msg: str =''):
        """
        等待元素出现
        :param locator: 元素定位器，同时支持id,name,xpath,css
        :param timeout: 超时时间
        :return:
        """
        temp = locator
        try:
            if locator.startswith('//') or locator.startswith('xpath='):
                ls = eval('By.XPATH')
                if locator.startswith('xpath='):
                    locator = locator[locator.find('xpath=') + 6:locator.__len__()]
            elif locator.startswith('#') or locator.startswith('>') or locator.startswith('css='):
                ls = eval('By.CSS_SELECTOR')
                if locator.startswith('css='):
                    locator = locator[locator.find('xpath=') + 4:locator.__len__()]
            elif locator.startswith('id='):
                ls = eval('By.ID')
                locator = locator[locator.find('id=') + 3:locator.__len__()]
            elif locator.startswith('name='):
                ls = eval('By.NAME')
                locator = locator[locator.find('name=') +5:locator.__len__()]
            else:
                logger.error("温馨提示，目前只支持Xpath、Css、Id、name方式定位...")
            WebDriverWait(self.driver, int(timeout), 0.5).until(EC.presence_of_element_located((ls, locator)),
                                                                message=msg)
            self.__attach_info('', temp + ' ' + str(timeout) + ' ' + msg, 'True')
        except Exception as e:
            self.__attach_info('', temp + ' ' + str(timeout) + ' ' + msg, 'False')
            logger.exception(e)
            self.__get_screenshot()
            pytest.fail()

    def Wait_Until_Page_Contains(self, text: str = '', timeout: str = '5', msg: str = ''):
        """
        等待页面包含文本
        :param text:  指定文本
        :param timeout:  超时时间
        :param msg: 报错信息
        :return:
        """
        try:
            WebDriverWait(self.driver,int(timeout), 0.5).until(
                EC.text_to_be_present_in_element((By.XPATH, '//*'), text), message= msg)
            self.__attach_info('', text + ' ' + str(timeout) + ' ' + msg, 'True')
        except Exception as e:
            self.__attach_info('', text + ' ' + str(timeout) + ' ' + msg, 'False')



    def Title_Should_Be(self,title: str = '', msg: str =''):
        """
        断言，校验title与传入的title参数相同
        :param title: 传入的title校验参数
        :param msg: 报错信息
        :return:
        """

        try:
            WebDriverWait(self.driver, 10,0.5).until(EC.title_is(title), message=msg)
            self.__attach_info('',title + ' ' + msg, 'True')
        except Exception as e:
            self.__attach_info('', title + ' ' + msg, 'False')
            logger.exception(e)
            self.__get_screenshot()
            pytest.fail()

    def Title_Should_Contains(self, title: str = '', msg: str=''):
        """
        判断标题是否包含title
        :param title: 传入的title预期值
        :param msg: 报错信息
        :return:
        """

        try:
            WebDriverWait(self.driver, 15,0.5).until(EC.title_contains(title),message=msg)
            self.__attach_info('',title + ' ' + msg, 'True')
        except Exception as e:
            self.__attach_info('', title + ' ' + msg, 'False')
            logger.exception(e)
            self.__get_screenshot()
            pytest.fail()

    def Alert_Should_Be_Present(self, text:str = ''):
        """
        判断alter弹窗是否出现
        :param text: 断言内容
        :return:
        """

        try:
            WebDriverWait(self.driver, 15, 0.5).until(EC.alert_is_present(),message='没有出现指定弹窗')
            msg = EC.alert_is_present()(self.driver).text
            if text in msg:
                self.__attach_info('', text, '\'' + text + '\' in \'' + msg + '\'')
                self.driver.switch_to.alert.accept()
        except Exception as e:
            self.__attach_info('', text, 'Alert message should have been \'' + text + '\' but it was \'' + msg + '\'.')
            logger.exception(e)
            self.__get_screenshot()
            pytest.fail()

    def Should_Be_Equal(self, actual: str = '', expect: str = ''):

        temp1 = actual
        temp2 = expect
        try:
            actual = actual.replace('${','').replace('}','')
            if str(actual) == str(expect):
                self.__attach_info('',temp1 + ' ' + temp2, 'True')
        except Exception as e:
            self.__attach_info('', temp1 + ' ' + temp2, 'False')

    # ------------------------------------------------变量相关———————————————————————————
    def Set_Variable(self, var: str == '', value: str = ''):
        # 取出全局变量
        ori_var = var
        ori_value = value
        values = self.__get_relations(value)

        # 去掉返回值中的变量符号
        var = var.replace('}', '').replace('${', '')

        self.relations[var] = values
        res = self.__get_relations('${' + var + '}')
        self.__attach_info(ori_var, ori_value, res)



    # ------------------------------------------------条件逻辑———————————————————————————

    def Run_Keyword_If(self, expression, kwd, *params):

        expression = self.__get_relations(expression)
        sta = eval(expression)
        if sta:
            self.ifstatus =None
            params = list(params)
            # print(2222222,params)